package com.google.code.swingonfire.showcase.utils;

import static com.google.code.swingonfire.builder.JComponentBuilders.*;

import java.awt.Component;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.Serializable;

import javax.swing.JPanel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import com.google.code.swingonfire.layout.GBC;
import com.google.code.swingonfire.utility.Prefs;

public class ParameterPanel extends JPanel implements ActionListener, ChangeListener
{

    private static final long serialVersionUID = -7470447908281571751L;

    private final ParameterDefinition<?, ?>[] parameterDefinitions;
    private final Component[] parameterComponents;

    private String actionCommand;

    public ParameterPanel(ParameterDefinition<?, ?>... parameterDefinitions)
    {
        super(new GridBagLayout());

        this.parameterDefinitions = parameterDefinitions;

        parameterComponents = new Component[parameterDefinitions.length];

        GBC gbc = new GBC(2, parameterDefinitions.length + 1);

        for (int i = 0; i < parameterDefinitions.length; i++)
        {
            ParameterDefinition<?, ?> definition = parameterDefinitions[i];
            Component component = definition.createComponent(this);

            parameterComponents[i] = component;

            if (component != null)
            {
                add(labelBuilder().text(definition.getName() + ":").labelFor(component).build(), gbc.right());
                add(component, gbc.next().hFill().weight(1));
                gbc.next();
            }
        }

        setOpaque(false);
    }

    public Component[] getParameterComponents()
    {
        return parameterComponents;
    }

    public Object[] getParameters()
    {
        Object[] result = new Object[parameterDefinitions.length];

        for (int i = 0; i < parameterDefinitions.length; i++)
        {
            result[i] = getParameter(i);
        }

        return result;
    }

    @SuppressWarnings("unchecked")
    public Object getParameter(int i)
    {
        @SuppressWarnings("rawtypes")
        ParameterDefinition definition = parameterDefinitions[i];

        return definition.getValue(parameterComponents[i]);
    }

    @SuppressWarnings("unchecked")
    public Object getParameter(String name)
    {
        for (int i = 0; i < parameterDefinitions.length; i++)
        {
            @SuppressWarnings("rawtypes")
            ParameterDefinition parameterDefinition = parameterDefinitions[i];

            if (name.equals(parameterDefinition.getName()))
            {
                return parameterDefinition.getValue(parameterComponents[i]);
            }
        }

        throw new IllegalArgumentException("Invalid parameter definition name: " + name);
    }

    public void addActionListener(ActionListener listener)
    {
        listenerList.add(ActionListener.class, listener);
    }

    public void removeActionListener(ActionListener listener)
    {
        listenerList.add(ActionListener.class, listener);
    }

    protected void fireAction()
    {
        Object[] listeners = listenerList.getListenerList();
        ActionEvent e = new ActionEvent(this, 0, actionCommand, System.currentTimeMillis(), 0);

        for (int i = listeners.length - 2; i >= 0; i -= 2)
        {
            if (listeners[i] == ActionListener.class)
            {
                ((ActionListener) listeners[i + 1]).actionPerformed(e);
            }
        }
    }

    public String getActionCommand()
    {
        return actionCommand;
    }

    public void setActionCommand(String actionCommand)
    {
        this.actionCommand = actionCommand;
    }

    @SuppressWarnings("unchecked")
    public void backup(Prefs prefs)
    {
        for (int i = 0; i < parameterDefinitions.length; i += 1)
        {
            @SuppressWarnings("rawtypes")
            ParameterDefinition definition = parameterDefinitions[i];
            Object value = definition.getValue(parameterComponents[i]);

            if (Serializable.class.isAssignableFrom(definition.getParameterType()))
            {
                prefs.set(definition.getParameterType(), definition.getName(), (Serializable) value);
            }
        }
    }

    @SuppressWarnings("unchecked")
    public void restore(Prefs prefs)
    {
        for (int i = 0; i < parameterDefinitions.length; i += 1)
        {
            @SuppressWarnings("rawtypes")
            ParameterDefinition definition = parameterDefinitions[i];

            if (Serializable.class.isAssignableFrom(definition.getParameterType()))
            {
                Object value = prefs.get(definition.getParameterType(), definition.getName(), null);

                if (value != null)
                {
                    definition.setValue(parameterComponents[i], value);
                }
            }
        }
    }

    @Override
    public void actionPerformed(ActionEvent e)
    {
        fireAction();
    }

    @Override
    public void stateChanged(ChangeEvent e)
    {
        fireAction();
    }

}
